/*
 * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.dtv.lwuit;

import com.sun.dtv.lwuit.geom.Rectangle;
import com.sun.dtv.lwuit.geom.Dimension;
import com.sun.dtv.lwuit.plaf.Style;
import com.sun.dtv.lwuit.animations.Animation;
import com.sun.dtv.lwuit.animations.Motion;
import com.sun.dtv.lwuit.events.FocusListener;
import com.sun.dtv.lwuit.events.StyleListener;
import com.sun.dtv.lwuit.plaf.Border;
import com.sun.dtv.lwuit.plaf.LookAndFeel;
import com.sun.dtv.lwuit.plaf.UIManager;
import com.sun.dtv.ui.Animated;
import java.util.Hashtable;

/**
 * Base class for all the widgets in the toolkit using the composite pattern in
 * a similar way to the AWT Container/Component relationship. All components are
 * potentially animated (need to be registered in {@link Display}).
 *
 * @author Chen Fishbein
 * Formato JAVA DTV 1.1 por
 * @author Leonardo Baptista
 */
public class Component implements Animation, StyleListener, Animated {

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the down key
     */
    private Component nextFocusDown;
    private Component nextFocusUp;
    /**
     * Indicates whether component is enabled or disabled
     */
    private boolean enabled = true;
    /**
     * Allows us to determine which component will receive focus next when travering
     * with the right key
     */
    private Component nextFocusRight;
    private Component nextFocusLeft;
    /**
     * Baseline resize behavior constant used to properly align components.
     * Indicates as the size of the component
     * changes the baseline remains a fixed distance from the top of the
     * component.
     * @see #getBaselineResizeBehavior
     */
    public static final int BRB_CONSTANT_ASCENT = 1;
    /**
     * Baseline resize behavior constant used to properly align components. Indicates as the size of the component
     * changes the baseline remains a fixed distance from the bottom of the
     * component.
     * @see #getBaselineResizeBehavior
     */
    public static final int BRB_CONSTANT_DESCENT = 2;
    /**
     * Baseline resize behavior constant used to properly align components. Indicates as the size of the component
     * changes the baseline remains a fixed distance from the center of the
     * component.
     * @see #getBaselineResizeBehavior
     */
    public static final int BRB_CENTER_OFFSET = 3;
    /**
     * Baseline resize behavior constant used to properly align components. Indicates as the size of the component
     * changes the baseline can not be determined using one of the other
     * constants.
     * @see #getBaselineResizeBehavior
     */
    public static final int BRB_OTHER = 4;
    private boolean visible = true;
    /**
     * Used as an optimization to mark that this component is currently being
     * used as a cell renderer
     */
    private boolean cellRenderer;
    /**
     * Indicates that this component is fixed into place and not affected by
     * scrolling of the parent container. This is applicable for components such as
     * menus etc...
     */
    private boolean fixedPosition;
    private Rectangle bounds = new Rectangle(0, 0, new Dimension(0, 0));
    private int scrollX;
    private int scrollY;
    private Dimension preferredSize;
    private Style style;
    private Container parent;
    private boolean focused = false;
    private boolean focusPainted = true;
    private EventDispatcher focusListeners = new EventDispatcher();
    private boolean handlesInput = false;
    private boolean shouldCalcPreferredSize = true;
    private boolean focusable = true;
    private boolean isScrollVisible = true;
    /**
     * Indicates that moving through the component should work as an animation
     */
    private boolean smoothScrolling;
    /**
     * Animation speed in milliseconds allowing a developer to slow down or accelerate
     * the smooth animation mode
     */
    private int animationSpeed;
    private Motion animationMotion;
    private Motion draggedMotion;
    /**
     * Allows us to flag a drag operation in action thus preventing the mouse pointer
     * release event from occuring.
     */
    private boolean dragActivated;
    private int initialScrollY = -1;
    private int destScrollY = -1;
    private int lastScrollY;
    private int beforeLastScrollY;
    private long[] lastTime = new long[2];
    private int[] lastDragged = new int[2];
    private int pLastDragged = 0;
    /**
     * Indicates if the component is in the initalized state, a component is initialized
     * when its initComponent() method was invoked. The initMethod is invoked before showing the
     * component to the user.
     */
    private boolean initialized;
    /**
     * Indicates a Component center alignment
     */
    public static final int CENTER = 4;
    /**
     * Box-orientation constant used to specify the top of a box.
     */
    public static final int TOP = 0;
    /**
     * Box-orientation constant used to specify the left side of a box.
     */
    public static final int LEFT = 1;
    /**
     * Box-orientation constant used to specify the bottom of a box.
     */
    public static final int BOTTOM = 2;
    /**
     * Box-orientation constant used to specify the right side of a box.
     */
    public static final int RIGHT = 3;
    private Hashtable clientProperties;
    private Rectangle dirtyRegion = null;

    /**
     * Creates a new instance of Component
     */
    protected Component() {
        style = UIManager.getInstance().getComponentStyle(getUIID());
        if (style != null) {
            style.addStyleListener(this);
            style.setBgPainter(new BGPainter());
        }
        LookAndFeel laf = UIManager.getInstance().getLookAndFeel();
        animationSpeed = laf.getDefaultSmoothScrollingSpeed();
        setSmoothScrolling(laf.isDefaultSmoothScrolling());
    }

    /**
     * Returns the current component x location relatively to its parent container
     *
     * Presente no JAVADTV 1.1
     * @return the current x coordinate of the components origin
     */
    public int getX() {
        return bounds.getX();
    }

    /**
     * Returns the component y location relatively to its parent container
     *
     * Presente no JAVADTV 1.1
     * @return the current y coordinate of the components origin
     */
    public int getY() {
        return bounds.getY();
    }

    /**
     * Returns whether the component is visible or not
     *
     * Presente no JAVADTV 1.1
     * @return true if component is visible; otherwise false
     */
    public boolean isVisible() {
        return visible;
    }

    /**
     * Client properties allow the association of meta-data with a component, this
     * is useful for some applications that construct GUI's on the fly and need
     * to track the connection between the UI and the data.
     *
     * Presente no JAVADTV 1.1
     * @param key the key used for putClientProperty
     * @return the value set to putClientProperty or null if no value is set to the property
     */
    public Object getClientProperty(String key) {
        if (clientProperties == null) {
            return null;
        }
        return clientProperties.get(key);
    }

    /**
     * Client properties allow the association of meta-data with a component, this
     * is useful for some applications that construct GUI's on the fly and need
     * to track the connection between the UI and the data. Setting the value to
     * null will remove the client property from the component.
     *
     * Presente no JAVADTV 1.1
     * @param key arbitrary key for the property
     * @param value the value assigned to the given client property
     */
    public void putClientProperty(String key, Object value) {
        if (clientProperties == null) {
            if (value == null) {
                return;
            }
            clientProperties = new Hashtable();
        }
        if (value == null) {
            clientProperties.remove(key);
            if (clientProperties.size() == 0) {
                clientProperties = null;
            }
        } else {
            clientProperties.put(key, value);
        }
    }

    /**
     * gets the Component dirty region
     * OK
     * @return
     */
    Rectangle getDirtyRegion() {
        return dirtyRegion;
    }

    /**
     * sets the Component dirty region
     * OK
     * @param dirty
     */
    void setDirtyRegion(Rectangle dirty) {
        this.dirtyRegion = dirty;
    }

    /**
     * Toggles visibility of the component
     *
     * Presente no JAVADTV 1.1
     * @param visible true if component is visible; otherwise false
     */
    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    /**
     * Returns the component width
     *
     * Presente no JAVADTV 1.1
     * @return the component width
     */
    public int getWidth() {
        return bounds.getSize().getWidth();
    }

    /**
     * Returns the component height
     *
     * Presente no JAVADTV 1.1
     * @return the component height
     */
    public int getHeight() {
        return bounds.getSize().getHeight();
    }

    /**
     * Sets the Component x location relative to the parent container, this method
     * is exposed for the purpose of external layout managers and should not be invoked
     * directly.
     *
     * Presente no JAVADTV 1.1
     * @param x the current x coordinate of the components origin
     */
    public void setX(int x) {
        bounds.setX(x);
    }

    /**
     * Sets the Component y location relative to the parent container, this method
     * is exposed for the purpose of external layout managers and should not be invoked
     * directly.
     *
     * Presente no JAVADTV 1.1
     * @param y the current y coordinate of the components origin
     */
    public void setY(int y) {
        bounds.setY(y);
    }

    /**
     * The baseline for the component text according to which it should be aligned
     * with other components for best visual look.
     *
     * Presente no JAVADTV 1.1
     * @param width the component width
     * @param height the component height
     * @return baseline value from the top of the component
     */
    public int getBaseline(int width, int height) {
        return getHeight() - getStyle().getPadding(BOTTOM);
    }

    /**
     * Returns a constant indicating how the baseline varies with the size
     * of the component.
     *
     * Presente no JAVADTV 1.1
     * @return one of BRB_CONSTANT_ASCENT, BRB_CONSTANT_DESCENT,
     *         BRB_CENTER_OFFSET or BRB_OTHER
     */
    public int getBaselineResizeBehavior() {
        return BRB_OTHER;
    }

    /**
     * Sets the Component Preferred Size, there is no garuntee the Component will
     * be sized at its Preferred Size. The final size of the component may be
     * smaller than its preferred size or even larger than the size.<br>
     * The Layout manager can take this value into consideration, but there is
     * no guarantee or requirement.
     *
     * Presente no JAVADTV 1.1
     * @param d the component dimension
     */
    public void setPreferredSize(Dimension d) {
        preferredSize().setWidth(d.getWidth());
        preferredSize().setHeight(d.getHeight());
        sizeRequestedByUser = true;
    }

    /**
     * Returns the Component Preferred Size, there is no garuntee the Component will
     * be sized at its Preferred Size. The final size of the component may be
     * smaller than its preferred size or even larger than the size.<br>
     * The Layout manager can take this value into consideration, but there is
     * no guarantee or requirement.
     *
     * Presente no JAVADTV 1.1
     * @return the component preferred size
     */
    public Dimension getPreferredSize() {
        return preferredSize();
    }

    /**
     * Sets the Component width, this method is exposed for the purpose of
     * external layout managers and should not be invoked directly.<br>
     * If a user wishes to effect the component size setPreferredSize should
     * be used.
     *
     * Presente no JAVADTV 1.1
     * @param width the width of the component
     * @see #setPreferredSize
     */
    public void setWidth(int width) {
        bounds.getSize().setWidth(width);
    }

    /**
     * Sets the Component height, this method is exposed for the purpose of
     * external layout managers and should not be invoked directly.<br>
     * If a user wishes to effect the component size setPreferredSize should
     * be used.
     *
     * Presente no JAVADTV 1.1
     * @param height the height of the component
     * @see #setPreferredSize
     */
    public void setHeight(int height) {
        bounds.getSize().setHeight(height);
    }

    /**
     * Sets the Component size, this method is exposed for the purpose of
     * external layout managers and should not be invoked directly.<br>
     * If a user wishes to effect the component size setPreferredSize should
     * be used.
     *
     * Presente no JAVADTV 1.1
     * @param d the component dimension
     * @see #setPreferredSize
     */
    public void setSize(Dimension d) {
        Dimension d2 = bounds.getSize();
        d2.setWidth(d.getWidth());
        d2.setHeight(d.getHeight());
    }

    /**
     * Unique identifier for a component, must be overriden for a component so
     * a style can be applied to the component
     *
     * Nao presente no JAVADTV 1.1 - Se alguem estender uma classe e usar pode dar pau.
     * OK - NAO DA PRA ESTENDER MAIS
     * @return unique string identifying this component for the style sheet
     */
    String getUIID() {
        return null;
    }

    /**
     * Returns the container in which this component is contained
     *
     * Presente no JAVADTV 1.1
     * @return the parent container in which this component is contained
     */
    public Container getParent() {
        return parent;
    }

    /**
     * Sets the Component Parent.
     * This method should not be called by the user.
     *
     * OK
     * @param parent the parent container
     */
    void setParent(Container parent) {
        this.parent = parent;
    }

    /**
     * Registers interest in receiving callbacks for focus gained events, a focus event
     * is invoked when the component accepts the focus. A special case exists for the
     * Form which sends a focus even for every selection within the form.
     *
     * Presente no JAVADTV 1.1
     * @param l listener interface implementing the observable pattern
     */
    public void addFocusListener(FocusListener l) {
        focusListeners.addListener(l);
    }

    /**
     * Deregisters interest in receiving callbacks for focus gained events
     *
     * Presente no JAVADTV 1.1
     * @param l listener interface implementing the observable pattern
     */
    public void removeFocusListener(FocusListener l) {
        focusListeners.removeListener(l);
    }

    /**
     * When working in 3 softbutton mode "fire" key (center softbutton) is sent to this method
     * in order to allow 3 button devices to work properly. When overriding this method
     * you should also override isSelectableInteraction to indicate that a command is placed
     * appropriately on top of the fire key for 3 soft button phones.
     *
     * Nao presente no JAVADTV 1.1
     * OK - NAO DA PRA ESTENDER MAIS
     */
    void fireClicked() {
    }

    /**
     * This method allows a component to indicate that it is interested in an "implicit" select
     * command to appear in the "fire" button when 3 softbuttons are defined in a device.
     *
     * Nao Presente no JAVADTV 1.1
     * OK - NAO DA PRA ESTENDER MAIS
     */
    boolean isSelectableInteraction() {
        return false;
    }

    /**
     * Fired when component gains focus
     *
     * OK
     */
    void fireFocusGained() {
        fireFocusGained(this);
    }

    /**
     * Fired when component lost focus
     *
     * OK
     */
    void fireFocusLost() {
        fireFocusLost(this);
    }

    /**
     * Fired when component gains focus
     *
     * OK
     */
    void fireFocusGained(Component cmp) {
        if (cmp.isCellRenderer()) {
            return;
        }

        focusListeners.fireFocus(cmp);
        focusGainedInternal();
        if (isSelectableInteraction()) {
            Form f = getComponentForm();
            if (f != null) {
                f.addSelectCommand();
            }
        }
    }

    /**
     * Fired when component lost focus
     *
     * OK
     */
    void fireFocusLost(Component cmp) {
        if (cmp.isCellRenderer()) {
            return;
        }
        if (isSelectableInteraction()) {
            Form f = getComponentForm();
            if (f != null) {
                f.removeSelectCommand();
            }
        }

        focusListeners.fireFocus(cmp);
        focusLostInternal();
    }

    /**
     * This method allows us to detect an action event internally without
     * implementing the action listener interface.
     *
     * OK
     */
    void fireActionEvent() {
    }

    /**
     * This method is useful since it is not a part of the public API yet
     * allows a component within this package to observe focus events
     * without implementing a public interface or creating a new class
     *
     * OK
     */
    void focusGainedInternal() {
    }

    /**
     * This method is useful since it is not a part of the public API yet
     * allows a component within this package to observe focus events
     * without implementing a public interface or creating a new class
     *
     * OK
     */
    void focusLostInternal() {
    }

    /**
     * This method paints all the parents Components Background.
     *
     * @param g the graphics object
     *
     * Presente no JAVADTV 1.1
     */
    public void paintBackgrounds(Graphics g) {
        Rectangle bounds = new Rectangle(getAbsoluteX(), getAbsoluteY(),
                getWidth(), getHeight());
        drawPainters(g, this.getParent(), this, bounds);
    }

    /**
     * Returns the absolute X location based on the component hierarchy, this method
     * calculates a location on the screen for the component rather than a relative
     * location as returned by getX()
     *
     * Presente no JAVADTV 1.1
     * @return the absolute x location of the component
     * @see #getX
     */
    public int getAbsoluteX() {
        int x = getX() - getScrollX();
        Container parent = getParent();
        if (parent != null) {
            x += parent.getAbsoluteX();
        }
        return x;
    }

    /**
     * Returns the absolute Y location based on the component hierarchy, this method
     * calculates a location on the screen for the component rather than a relative
     * location as returned by getX()
     *
     * Presente no JAVADTV 1.1
     * @return the absolute y location of the component
     * @see #getY
     */
    public int getAbsoluteY() {
        int y = getY() - getScrollY();
        Container parent = getParent();
        if (parent != null) {
            y += parent.getAbsoluteY();
        }
        return y;
    }

    /**
     * This method performs the paint of the component internally including drawing
     * the scrollbars and scrolling the component. This functionality is hidden
     * from developers to prevent errors
     *
     * OK
     * @param g the component graphics
     */
    final void paintInternal(Graphics g) {
        paintInternal(g, true);
    }

    /**
     *
     * @param g
     * @param paintIntersects
     *
     * OK
     */
    final void paintInternal(Graphics g, boolean paintIntersects) {
        if (!isVisible()) {
            return;
        }
        int oX = g.getClipX();
        int oY = g.getClipY();
        int oWidth = g.getClipWidth();
        int oHeight = g.getClipHeight();
        if (bounds.intersects(oX, oY, oWidth, oHeight)) {
            g.clipRect(getX(), getY(), getWidth(), getHeight());
            paintBackground(g);

            if (isScrollable()) {
                int scrollX = getScrollX();
                int scrollY = getScrollY();
                g.translate(-scrollX, -scrollY);
                paint(g);
                g.translate(scrollX, scrollY);
                if (isScrollVisible) {
                    paintScrollbars(g);
                }
            } else {
                paint(g);
            }
            if (getStyle().getBorder() != null) {
                paintBorder(g);
            }

            //paint all the intersecting Components above the Component
            if (paintIntersects && parent != null) {
                paintIntersectingComponentsAbove(g);
            }

            g.setClip(oX, oY, oWidth, oHeight);
        }
    }

    private void paintIntersectingComponentsAbove(Graphics g) {
        Container parent = getParent();
        Component component = this;
        Rectangle bounds = new Rectangle(getAbsoluteX(), getAbsoluteY(),
                getWidth(), getHeight());
        int tx = g.getTranslateX();
        int ty = g.getTranslateY();

        g.translate(-tx, -ty);
        while (parent != null) {
            g.translate(parent.getAbsoluteX() + parent.getScrollX(),
                    parent.getAbsoluteY() + parent.getScrollY());
            parent.paintIntersecting(g, component, bounds, true);
            g.translate(-parent.getAbsoluteX() - parent.getScrollX(),
                    -parent.getAbsoluteY() - parent.getScrollY());
            component = parent;
            parent = parent.getParent();
        }
        g.translate(tx, ty);

    }

    /**
     * Paints the UI for the scrollbars on the component, this will be invoked only
     * for scrollable components. This method invokes the appropriate X/Y versions
     * to do all the work.
     *
     * @param g the component graphics
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     */
    void paintScrollbars(Graphics g) {
        if (isScrollableX()) {
            paintScrollbarX(g);
        }
        if (isScrollableY()) {
            paintScrollbarY(g);
        }
    }

    /**
     * Paints the UI for the scrollbar on the X axis, this method allows component
     * subclasses to customize the look of a scrollbar
     *
     * @param g the component graphics
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     */
    void paintScrollbarX(Graphics g) {
        float offset = ((float) getScrollX()) / ((float) getPreferredSize().getWidth());
        float block = ((float) getWidth()) / ((float) getPreferredSize().getWidth());
        UIManager.getInstance().getLookAndFeel().drawHorizontalScroll(g, this, offset, block);
    }

    /**
     * Paints the UI for the scrollbar on the Y axis, this method allows component
     * subclasses to customize the look of a scrollbar
     *
     * @param g the component graphics
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     */
    void paintScrollbarY(Graphics g) {
        float offset = ((float) getScrollY()) / ((float) getPreferredSize().getHeight());
        float block = ((float) getHeight()) / ((float) getPreferredSize().getHeight());
        UIManager.getInstance().getLookAndFeel().drawVerticalScroll(g, this, offset, block);
    }

    /**
     * Paints this component as a root by going to all the parent components and
     * setting the absolute translation based on coordinates and scroll status.
     * Restores translation when the painting is finished.
     *
     * Presente no JAVADTV 1.1
     * @param g the graphics to paint this Component on
     *
     */
    final public void paintComponent(Graphics g) {
        paintComponent(g, true);
    }

    /**
     * Paints this component as a root by going to all the parent components and
     * setting the absolute translation based on coordinates and scroll status.
     * Restores translation when the painting is finished.
     *
     * @param g the graphics to paint this Component on
     * @param background if true paints all parents background
     *
     * Presente no JAVADTV 1.1
     */
    final public void paintComponent(Graphics g, boolean background) {
        int clipX = g.getClipX();
        int clipY = g.getClipX();
        int clipW = g.getClipWidth();
        int clipH = g.getClipHeight();
        Container parent = getParent();
        int translateX = 0;
        int translateY = 0;
        if (!isFixedPosition()) {
            while (parent != null) {
                translateX += parent.getX();
                translateY += parent.getY();
                //if (parent.isScrollable()) {
                if (parent.isScrollableX()) {
                    translateX -= parent.getScrollX();
                }
                if (parent.isScrollableY()) {
                    translateY -= parent.getScrollY();
                }
                // since scrollability can translate everything... we should clip based on the
                // current scroll
                g.clipRect(parent.getAbsoluteX() + parent.getScrollX(), parent.getAbsoluteY() + parent.getScrollY(), parent.getWidth(), parent.getHeight());
                //}
                // restore the position for fixed components
                if (parent.isFixedPosition()) {
                    translateX = parent.getX();
                    translateY = parent.getY();
                    g.clipRect(parent.getX(), parent.getY(), parent.getWidth(), parent.getHeight());
                    break;
                }
                parent = parent.getParent();
            }
        }
        g.clipRect(translateX + getX(), translateY + getY(), getWidth(), getHeight());
        if (background) {
            paintBackgrounds(g);
        }

        g.translate(translateX, translateY);
        paintInternal(g);
        g.translate(-translateX, -translateY);

        Form parentForm = getComponentForm();
        if (parentForm != null) {
            Painter glass = parentForm.getGlassPane();
            if (glass != null) {
                glass.paint(g, parentForm.getBounds());
            }
        }

        g.setClip(clipX, clipY, clipW, clipH);
    }

    private void drawPainters(com.sun.dtv.lwuit.Graphics g, Component par, Component c,
            Rectangle bounds) {
        if (par == null) {
            return;
        } else {
            if (par.getStyle().getBgTransparency() != ((byte) 0xFF)) {
                drawPainters(g, par.getParent(), par, bounds);
            }
        }

        int transX = par.getAbsoluteX() + par.getScrollX();
        int transY = par.getAbsoluteY() + par.getScrollY();

        g.translate(transX, transY);

        ((Container) par).paintIntersecting(g, c, bounds, false);

        if (par.getStyle().getBorder() != null) {
            Border b = par.getBorder();
            if (b != null && b.isBackgroundPainter()) {
                g.translate(-par.getX(), -par.getY());
                b.paintBorderBackground(g, par);
                b.paint(g, par);
                g.translate(par.getX() - transX, par.getY() - transY);
            }
        } else {
            Painter p = par.getStyle().getBgPainter();
            if (p != null) {
                p.paint(g, new Rectangle(0, 0, par.getWidth(), par.getHeight()));
            }
            g.translate(-transX, -transY);
        }
    }

    /**
     * Normally returns getStyle().getBorder() but some subclasses might use this
     * to programmatically replace the border in runtime e.g. for a pressed border effect
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @return the borde that is drawn according to the current component state
     */
    Border getBorder() {
        Border b = getStyle().getBorder();
        if (hasFocus()) {
            if (b != null) {
                return b.getFocusedInstance();
            }
            return b;
        } else {
            return b;
        }
    }

    /**
     * Paints the background of the component, invoked with the clipping region
     * and appropriate scroll translation.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param g the component graphics
     */
    void paintBackground(Graphics g) {
        if (getStyle().getBorder() != null) {
            Border b = getBorder();
            if (b != null && b.isBackgroundPainter()) {
                b.paintBorderBackground(g, this);
                return;
            }
        }
        if (getStyle().getBgPainter() != null) {
            getStyle().getBgPainter().paint(g, new Rectangle(getX(), getY(), getWidth(), getHeight()));
        }
    }

    /**
     * This method paints the Component on the screen, it should be overriden
     * by subclasses to perform custom drawing or invoke the UI API's to let
     * the PLAF perform the rendering.
     *
     * Presente no JAVADTV 1.1
     * @param g the component graphics
     */
    public void paint(Graphics g) {
    }

    /**
     * Indicates whether the component should/could scroll by default a component
     * is not scrollable.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @return whether the component is scrollable
     */
    boolean isScrollable() {
        return isScrollableX() || isScrollableY();
    }

    /**
     * Indicates whether the component should/could scroll on the X axis
     *
     * Presente no JAVADTV 1.1
     * @return whether the component is scrollable on the X axis
     */
    public boolean isScrollableX() {
        return false;
    }

    /**
     * Indicates whether the component should/could scroll on the Y axis
     *
     * Presente no JAVADTV 1.1
     * @return whether the component is scrollable on the X axis
     */
    public boolean isScrollableY() {
        return false;
    }

    /**
     * Indicates the X position of the scrolling, this number is relative to the
     * component position and so a position of 0 would indicate the x position
     * of the component.
     *
     * Presente no JAVADTV 1.1
     * @return the X position of the scrolling
     */
    public int getScrollX() {
        return scrollX;
    }

    /**
     * Indicates the Y position of the scrolling, this number is relative to the
     * component position and so a position of 0 would indicate the x position
     * of the component.
     *
     * Presente no JAVADTV 1.1
     * @return the Y position of the scrolling
     */
    public int getScrollY() {
        return scrollY;
    }

    /**
     * Indicates the X position of the scrolling, this number is relative to the
     * component position and so a position of 0 would indicate the x position
     * of the component.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param scrollX the X position of the scrolling
     */
    void setScrollX(int scrollX) {
        if (isScrollableX()) {
            this.scrollX = scrollX;
            repaint();
        }
    }

    /**
     * Indicates the X position of the scrolling, this number is relative to the
     * component position and so a position of 0 would indicate the x position
     * of the component.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param scrollY the Y position of the scrolling
     */
    void setScrollY(int scrollY) {
        if (isScrollableY()) {
            this.scrollY = scrollY;
            repaint();
        }
    }

    /**
     * Returns the gap to be left for the bottom scrollbar on the X axis. This
     * method is used by layout managers to determine the room they should
     * leave for the scrollbar
     *
     * Presente no JAVADTV 1.1
     * @return the gap to be left for the bottom scrollbar on the X axis
     */
    public int getBottomGap() {
        if (isScrollableX()) {
            return UIManager.getInstance().getLookAndFeel().getHorizontalScrollHeight();
        }
        return 0;
    }

    /**
     * Returns the gap to be left for the side scrollbar on the Y axis. This
     * method is used by layout managers to determine the room they should
     * leave for the scrollbar. (note: side scrollbar rather than left scrollbar
     * is used for a future version that would support bidi).
     *
     * Presente no JAVADTV 1.1
     * @return the gap to be left for the side scrollbar on the Y axis
     */
    public int getSideGap() {
        if (isScrollableY()) {
            return UIManager.getInstance().getLookAndFeel().getVerticalScrollWidth();
        }
        return 0;
    }

    /**
     * Retuns true if the given absolute cordinate is contained in the Component
     *
     * Presente no JAVADTV 1.1
     * @param x the given absolute x cordinate
     * @param y the given absolute y cordinate
     * @return true if the given absolute cordinate is contained in the
     * Component; otherwise false
     */
    public boolean contains(int x, int y) {
        int absX = getAbsoluteX() + getScrollX();
        int absY = getAbsoluteY() + getScrollY();
        return (x >= absX && x < absX + getWidth() && y >= absY && y < absY + getHeight());
    }

    /**
     * Calculates the preferred size based on component content. This method is
     * invoked lazily by getPreferred size.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @return the calculated preferred size based on component content
     */
    Dimension calcPreferredSize() {
        Dimension d = new Dimension(0, 0);
        return d;
    }
    private boolean sizeRequestedByUser = false;

    private Dimension preferredSize() {

        if (!sizeRequestedByUser && (shouldCalcPreferredSize || preferredSize == null)) {
            shouldCalcPreferredSize = false;
            preferredSize = calcPreferredSize();
        }
        return preferredSize;

    }

    /**
     * Returns the component bounds which is sometimes more convenient than invoking
     * getX/Y/Width/Height. Bounds are relative to parent container.<br>
     * Changing values within the bounds can lead to unpredicted behavior.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @see #getX
     * @see #getY
     * @return the component bounds
     */
    Rectangle getBounds() {
        return bounds;
    }

    /**
     * Returns true if this component can receive focus and is enabled
     *
     * Presente no JAVADTV 1.1
     * @return true if this component can receive focus; otherwise false
     */
    public boolean isFocusable() {
        return focusable && enabled && isVisible();
    }

    /**
     * A simple setter to determine if this Component can get focused
     *
     * Presente no JAVADTV 1.1
     * @param focusable indicate whether this component can get focused
     */
    public void setFocusable(boolean focusable) {
        this.focusable = focusable;
        Form p = getComponentForm();
        if (p != null) {
            p.clearFocusVectors();
        }
    }

    /**
     * Indicates the values within the component have changed and preferred
     * size should be recalculated
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param shouldCalcPreferredSize indicate whether this component need to
     * recalculate his preferred size
     */
    void setShouldCalcPreferredSize(boolean shouldCalcPreferredSize) {
        if (shouldCalcPreferredSize != this.shouldCalcPreferredSize) {
            this.shouldCalcPreferredSize = shouldCalcPreferredSize;
            if (shouldCalcPreferredSize && getParent() != null) {
                this.shouldCalcPreferredSize = shouldCalcPreferredSize;
                getParent().setShouldCalcPreferredSize(shouldCalcPreferredSize);
            }
        }
    }

    /**
     * Indicates whether focus should be drawn around the component or whether
     * it will handle its own focus painting
     *
     * Presente no JAVADTV 1.1
     * @return true if focus should be drawn around the component
     * ; otherwise false
     */
    public boolean isFocusPainted() {
        return focusPainted;
    }

    /**
     * Indicates whether focus should be drawn around the component or whether
     * it will handle its own focus painting
     *
     * Presente no JAVADTV 1.1
     * @param focusPainted indicates whether focus should be drawn around the
     * component
     */
    public void setFocusPainted(boolean focusPainted) {
        this.focusPainted = focusPainted;
    }

    /**
     * Prevents key events from being grabbed for focus traversal. E.g. a list component
     * might use the arrow keys for internal navigation so it will switch this flag to
     * true in order to prevent the focus manager from moving to the next component.
     *
     * Presente no JAVADTV 1.1
     * @return true if key events are being used for focus traversal
     * ; otherwise false
     */
    public boolean handlesInput() {
        return handlesInput;
    }

    /**
     * Prevents key events from being grabbed for focus traversal. E.g. a list component
     * might use the arrow keys for internal navigation so it will switch this flag to
     * true in order to prevent the focus manager from moving to the next component.
     *
     * Presente no JAVADTV 1.1
     * @param handlesInput indicates whether key events can be grabbed for
     * focus traversal
     */
    public void setHandlesInput(boolean handlesInput) {
        this.handlesInput = handlesInput;
    }

    /**
     * Returns true if the componet has focus
     *
     * Presente no JAVADTV 1.1
     * @return true if the componet has focus; otherwise false
     * @see #setFocus
     */
    public boolean hasFocus() {
        return focused;
    }

    /**
     * This flag doesn't really give focus, its a state that determines
     * what colors from the Style should be used when painting the component.
     * Actual focus is determined by the parent form
     *
     * Presente no JAVADTV 1.1
     * @param focused sets the state that determines what colors from the
     * Style should be used when painting a focused component
     *
     * @see #requestFocus
     */
    public void setFocus(boolean focused) {
        this.focused = focused;
    }

    /**
     * Returns the Component Form or null if this Component
     * is not added yet to a form
     *
     * Presente no JAVADTV 1.1
     * @return the Component Form
     */
    public Form getComponentForm() {
        Form retVal = null;
        Component parent = getParent();
        if (parent != null) {
            retVal = parent.getComponentForm();
        }
        return retVal;
    }

    /**
     * Repaint the given component to the screen
     *
     * OK
     * @param cmp the given component on the screen
     */
    void repaint(Component cmp) {
        if (isCellRenderer() || cmp.getWidth() <= 0 || cmp.getHeight() <= 0) {
            return;
        }
        // null parent repaint can happen when a component is removed and modified which
        // is common ofr a popup
        Component parent = getParent();
        if (parent != null) {
            parent.repaint(cmp);
        }
    }

    /**
     * Repaint this Component, the repaint call causes a callback of the paint
     * method on the event dispatch thread.
     *
     * Presente no JAVADTV 1.1
     * @see Display
     */
    public void repaint() {
        dirtyRegion = null;
        repaint(this);
    }

    /**
     *
     * Presente no JAVADTV 1.1
     * @param x
     * @param y
     * @param w
     * @param h
     */
    public void repaint(int x, int y, int w, int h) {
        if (dirtyRegion == null) {
            dirtyRegion = new Rectangle(x, y, w, h);
        } else {
            Dimension size = dirtyRegion.getSize();

            int x1 = Math.min(dirtyRegion.getX(), x);
            int y1 = Math.min(dirtyRegion.getY(), y);

            int x2 = Math.max(x + w, dirtyRegion.getX() + size.getWidth());
            int y2 = Math.max(y + h, dirtyRegion.getY() + size.getHeight());

            dirtyRegion.setX(x1);
            dirtyRegion.setY(y1);
            size.setWidth(x2 - x1);
            size.setHeight(y2 - y1);

        }
        repaint(this);
    }

    /**
     * If this Component is focused, the key pressed event
     * will call this method
     *
     * Presente no JAVADTV 1.1
     * @param keyCode the key code value to indicate a physical key.
     */
    public void keyPressed(int keyCode) {
    }

    /**
     * If this Component is focused, the key released event
     * will call this method
     *
     * Presente no JAVADTV 1.1
     * @param keyCode the key code value to indicate a physical key.
     */
    public void keyReleased(int keyCode) {
    }

    /**
     * If this Component is focused, the key repeat event
     * will call this method. Calls key pressed/released by default
     *
     * Presente no JAVADTV 1.1
     * @param keyCode the key code value to indicate a physical key.
     */
    public void keyRepeated(int keyCode) {
        int game = Display.getInstance().getGameAction(keyCode);
        if (game == Display.GAME_DOWN || game == Display.GAME_UP || game == Display.GAME_LEFT || game == Display.GAME_RIGHT) {
            keyPressed(keyCode);
            keyReleased(keyCode);
        }
    }

    /**
     * Allows defining the physics for the animation motion behavior directly
     * by plugging in an alternative motion object
     *
     * @param motion new motion object
     */
    private void setAnimationMotion(Motion motion) {
        animationMotion = motion;
    }

    /**
     * Allows defining the physics for the animation motion behavior directly
     * by plugging in an alternative motion object
     *
     * @return the component motion object
     */
    private Motion getAnimationMotion() {
        return animationMotion;
    }

    /**
     * Scroll animation speed in milliseconds allowing a developer to slow down or accelerate
     * the smooth animation mode
     *
     * Presente no JAVADTV 1.1
     * @return scroll animation speed in milliseconds
     */
    public int getScrollAnimationSpeed() {
        return animationSpeed;
    }

    /**
     * Scroll animation speed in milliseconds allowing a developer to slow down or accelerate
     * the smooth animation mode
     *
     * Presente no JAVADTV 1.1
     * @param animationSpeed scroll animation speed in milliseconds
     */
    public void setScrollAnimationSpeed(int animationSpeed) {
        this.animationSpeed = animationSpeed;
    }

    /**
     * Indicates that scrolling through the component should work as an animation
     *
     * Presente no JAVADTV 1.1
     * @return whether this component use smooth scrolling
     */
    public boolean isSmoothScrolling() {
        return smoothScrolling;
    }

    /**
     * Indicates that scrolling through the component should work as an animation
     *
     * Presente no JAVADTV 1.1
     * @param smoothScrolling indicates if a component uses smooth scrolling
     */
    public void setSmoothScrolling(boolean smoothScrolling) {
        this.smoothScrolling = smoothScrolling;
        Form f = getComponentForm();
        if (f != null) {
            if (smoothScrolling) {
                f.registerAnimated(this);
            } else {
                f.deregisterAnimated(this);
            }
        }
    }

    /**
     * If this Component is focused, the pointer dragged event
     * will call this method
     *
     * Presente no JAVADTV 1.1
     * @param x the pointer x coordinate
     * @param y the pointer y coordinate
     */
    public void pointerDragged(int x, int y) {
        if (isScrollable() && isSmoothScrolling()) {
            int axisValue;
            if (isScrollableY()) {
                axisValue = y;
            } else {
                axisValue = x;
            }

            if (!dragActivated) {
                dragActivated = true;
                beforeLastScrollY = axisValue;
                lastScrollY = axisValue;
            }
            //save time and locations to create velocity when the
            //pointer is released
            long currentTime = System.currentTimeMillis();
            if (currentTime != lastTime[(pLastDragged + lastTime.length + 1) % lastTime.length]) {
                lastTime[pLastDragged] = System.currentTimeMillis();
                lastDragged[pLastDragged] = axisValue;
                pLastDragged = (++pLastDragged) % lastTime.length;
            }

            beforeLastScrollY = lastScrollY;
            lastScrollY = axisValue;

            // we drag inversly to get a feel of grabbing a physical screen
            // and pulling it in the reverse direction of the drag
            if (isScrollableY()) {
                int scroll = getScrollY() + (beforeLastScrollY - axisValue);
                if (scroll >= 0 && scroll < getPreferredSize().getHeight() - getHeight()) {
                    setScrollY(scroll);
                }
            } else {
                int scroll = getScrollX() + (beforeLastScrollY - axisValue);
                if (scroll >= 0 && scroll < getPreferredSize().getWidth() - getWidth()) {
                    setScrollX(scroll);
                }
            }
        } else {
            //try to find a scrollable element until you reach the Form
            Component parent = getParent();
            if (!(parent instanceof Form)) {
                parent.pointerDragged(x, y);
            }
        }
    }

    private void initScrollMotion() {
        Motion m = Motion.createLinearMotion(initialScrollY, destScrollY, getScrollAnimationSpeed());
        setAnimationMotion(m);
        m.start();
    }

    /**
     * If this Component is focused, the pointer pressed event
     * will call this method
     *
     * Presente no JAVADTV 1.1
     * @param x the pointer x coordinate
     * @param y the pointer y coordinate
     */
    public void pointerPressed(int x, int y) {
//        if(draggedMotion != null){
        draggedMotion = null;
//        }
    }

    /**
     * If this Component is focused, the pointer released event
     * will call this method
     *
     * Presente no JAVADTV 1.1
     * @param x the pointer x coordinate
     * @param y the pointer y coordinate
     */
    public void pointerReleased(int x, int y) {
        if (dragActivated) {
            long currentTime = System.currentTimeMillis();

            // replace x and y if this is an x scrolling container
            if (!isScrollableY()) {
                y = x;
            }

            if (currentTime != lastTime[(pLastDragged + lastTime.length + 1) % lastTime.length]) {
                lastTime[pLastDragged] = System.currentTimeMillis();
                lastDragged[pLastDragged] = y;
                pLastDragged = (++pLastDragged) % lastTime.length;
            }
            float velocity = (float) (lastDragged[pLastDragged] - lastDragged[(pLastDragged + lastDragged.length + 1) % lastDragged.length]) / (lastTime[pLastDragged] - lastTime[(pLastDragged + lastTime.length + 1) % lastTime.length]);
            velocity = velocity * -1;

            if (isScrollableY()) {
                draggedMotion = Motion.createFrictionMotion(scrollY, velocity, 0.0004f);
            } else {
                draggedMotion = Motion.createFrictionMotion(scrollX, velocity, 0.0004f);
            }
            draggedMotion.start();
            dragActivated = false;
        }
    }

    /**
     * Returns the Component Style allowing us to manipulate the look of the
     * component
     *
     * Presente no JAVADTV 1.1
     * @return the component Style object
     */
    public Style getStyle() {
        return style;
    }

    /**
     * Changes the Component Style by replacing the Component Style with the given Style
     *
     * Presente no JAVADTV 1.1
     * @param style the component Style object
     */
    public void setStyle(Style style) {
        if (this.style != null) {
            this.style.removeStyleListener(this);
        }
        this.style = style;
        this.style.addStyleListener(this);
        if (this.style.getBgPainter() == null) {
            this.style.setBgPainter(new BGPainter());
        }
        setShouldCalcPreferredSize(true);
        checkAnimation();
    }

    /**
     * Changes the current component to the focused component, will work only
     * for a component that belongs to a parent form.
     *
     * Presente no JAVADTV 1.1
     */
    public void requestFocus() {
        Form rootForm = getComponentForm();
        if (rootForm != null) {
            rootForm.requestFocus(this);
        }
    }

    /**
     * Overriden to return a useful value for debugging purposes
     *
     * Presente no JAVADTV 1.1
     * @return a string representation of this component
     */
    public String toString() {
        String className = getClass().getName();
        className = className.substring(className.lastIndexOf('.') + 1);
        return className + "[" + paramString() + "]";
    }

    /**
     * Returns a string representing the state of this component. This
     * method is intended to be used only for debugging purposes, and the
     * content and format of the returned string may vary between
     * implementations. The returned string may be empty but may not be
     * <code>null</code>.
     *
     * Nao Presente no JAVADTV 1.1 - Codigo usado para debug, sera removido
     * OK - NAO DA PRA ESTENDER MAIS
     * @return  a string representation of this component's state
     */
    String paramString() {
        return "x=" + getX() + " y=" + getY() + " width=" + getWidth() + " height=" + getHeight();
    }

    /**
     * Makes sure the component is up to date with the current style object
     *
     * Presente no JAVADTV 1.1
     */
    public void refreshTheme() {
        refreshTheme(getUIID());
    }

    /**
     * Makes sure the component is up to date with the given UIID
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param id The Style Id to update the Component with
     */
    void refreshTheme(String id) {
        if (style.isModified()) {
            style.merge(UIManager.getInstance().getComponentStyle(id));
        } else {
            setStyle(UIManager.getInstance().getComponentStyle(id));
        }
        checkAnimation();
        UIManager.getInstance().getLookAndFeel().bind(this);
    }

    /**
     * Internal method indicating whether we are in the middle of a drag
     *
     * OK
     * @return true if we are in the middle of a drag; otherwise false
     */
    boolean isDragActivated() {
        return dragActivated;
    }

    void checkAnimation() {
        Image bgImage = getStyle().getBgImage();
        if (bgImage != null && bgImage.isAnimation()) {
            Form parent = getComponentForm();

            if (parent != null) {
                parent.registerAnimated(this);
            }
        }
    }

    /**
     * @inheritDoc
     *
     * Presente no JAVADTV 1.1
     */
    public boolean animate() {
        Image bgImage = getStyle().getBgImage();
        boolean animateBackground = bgImage != null && bgImage.isAnimation() && ((Animation) bgImage).animate();
        Motion m = getAnimationMotion();
        //preform regular scrolling
        if (m != null && destScrollY != -1 && destScrollY != getScrollY()) {
            // change the variable directly for efficiency both in removing redundant
            // repaints and scroll checks
            scrollY = m.getValue();

            if (destScrollY == scrollY) {
                destScrollY = -1;
            }
            return true;
        }

        //preform the dragging motion if exists
        if (draggedMotion != null && !draggedMotion.isFinished()) {
            // change the variable directly for efficiency both in removing redundant
            // repaints and scroll checks
            int dragVal = draggedMotion.getValue();
            if (isScrollableY()) {
                if (dragVal >= 0 && dragVal <= (getPreferredSize().getHeight() - getHeight())) {
                    scrollY = dragVal;
                    return true;
                }
            } else {
                if (dragVal >= 0 && dragVal <= (getPreferredSize().getWidth() - getWidth())) {
                    scrollX = dragVal;
                    return true;
                }
            }
        }
        if (animateBackground && bgImage instanceof StaticAnimation) {
            Rectangle dirty = ((StaticAnimation) bgImage).getDirtyRegion();
            if (dirty != null) {
                dirty.setX(getAbsoluteX());
                dirty.setY(getAbsoluteY() + dirty.getY());
            }
            setDirtyRegion(dirty);
        }
        return animateBackground;
    }

    /**
     * Indicates that this component is fixed into place and not affected by
     * scrolling of the parent container. This is applicable for components such as
     * menus etc...
     *
     * OK
     * @return true is this component is fixed into place and not affected by
     * scrolling of the parent container; othewise false
     */
    boolean isFixedPosition() {
        return fixedPosition;
    }

    /**
     * Indicates that this component is fixed into place and not affected by
     * scrolling of the parent container. This is applicable for components such as
     * menus etc...
     *
     * OK
     * @param fixedPosition whether this component is fixed into place and not
     * affected by scrolling of the parent container
     */
    void setFixedPosition(boolean fixedPosition) {
        this.fixedPosition = fixedPosition;
    }

    /**
     * Makes sure the component is visible in the scroll if this container
     * is scrollable
     *
     * OK
     * @param rect the rectangle that need to be visible
     * @param coordinateSpace the component according to whose coordinates
     * rect is defined. Rect's x/y are relative to that component
     * (they are not absolute).
     */
    protected void scrollRectToVisible(Rectangle rect, Component coordinateSpace) {
        if (isScrollable()) {

            int scrollPosition = getScrollY();
            int w = getWidth() - getStyle().getPadding(LEFT) - getStyle().getPadding(RIGHT);
            int h = getHeight() - getStyle().getPadding(TOP) - getStyle().getPadding(BOTTOM);

            Rectangle view = new Rectangle(getScrollX(), getScrollY(), w, h);

            int relativeX = rect.getX();
            int relativeY = rect.getY();

            // component needs to be in absolute coordinates...
            Container parent = null;
            if (coordinateSpace != null) {
                parent = coordinateSpace.getParent();
            }
            if (parent == this) {
                if (view.contains(rect)) {
                    return;
                }
            } else {
                while (parent != this) {
                    // mostly a special case for list
                    if (parent == null) {
                        relativeX = rect.getX();
                        relativeY = rect.getY();
                        break;
                    }
                    relativeX += parent.getX();
                    relativeY += parent.getY();
                    parent = parent.getParent();
                }
                if (view.contains(relativeX, relativeY, rect.getSize().getWidth(), rect.getSize().getHeight())) {
                    return;
                }
            }
            if (getScrollX() > relativeX) {
                setScrollX(relativeX);
            }
            if (getScrollY() > relativeY) {
                scrollPosition = relativeY;
            }
            int rightX = relativeX + rect.getSize().getWidth();
            int bottomY = relativeY + rect.getSize().getHeight();
            if (getScrollX() + w < rightX) {
                setScrollX(getScrollX() + (rightX - (getScrollX() + w)));
            } else {
                if (getScrollX() > relativeX) {
                    setScrollX(relativeX);
                }
            }
            if (getScrollY() + h < bottomY) {
                scrollPosition = getScrollY() + (bottomY - (getScrollY() + h));
            } else {
                if (getScrollY() > relativeY) {
                    scrollPosition = relativeY;
                }
            }
            if (isSmoothScrolling()) {
                initialScrollY = getScrollY();
                destScrollY = scrollPosition;
                initScrollMotion();
            } else {
                setScrollY(scrollPosition);
            }
            repaint();
        }
    }

    /**
     * Draws the component border if such a border exists. The border unlike the content
     * of the component will not be affected by scrolling for a scrollable component.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param g graphics context on which the border is painted
     */
    void paintBorder(Graphics g) {
        Border b = getBorder();
        if (b != null) {
            if (isFocusPainted() && hasFocus()) {
                g.setColor(getStyle().getFgSelectionColor());
            } else {
                g.setColor(getStyle().getFgColor());
            }
            b.paint(g, this);
        }
    }

    /**
     * Used as an optimization to mark that this component is currently being
     * used as a cell renderer
     *
     * Presente no JAVADTV 1.1
     * @param cellRenderer indicate whether this component is currently being
     * used as a cell renderer
     */
    public void setCellRenderer(boolean cellRenderer) {
        this.cellRenderer = cellRenderer;
    }

    /**
     * Used as an optimization to mark that this component is currently being
     * used as a cell renderer
     *
     * OK
     * @return rtue is this component is currently being used as a cell renderer
     */
    boolean isCellRenderer() {
        return cellRenderer;
    }

    /**
     * Indicate whether this component scroll is visible
     *
     * Presente no JAVADTV 1.1
     * @return true is this component scroll is visible; otherwise false
     */
    public boolean isScrollVisible() {
        return isScrollVisible;
    }

    /**
     * Set whether this component scroll is visible
     *
     * Presente no JAVADTV 1.1
     * @param isScrollVisible Indicate whether this component scroll is visible
     */
    public void setIsScrollVisible(boolean isScrollVisible) {
        this.isScrollVisible = isScrollVisible;
    }

    /**
     * Invoked internally to initialize and bind the component
     *
     * OK
     */
    void initComponentImpl() {
        if (!initialized) {
            initialized = true;
            UIManager.getInstance().getLookAndFeel().bind(this);
            checkAnimation();
            initComponent();
        }
    }

    /**
     * Cleansup the initialization flags in the hierachy, notice that paint calls might
     * still occur after deinitilization mostly to perform transitions etc.
     * <p>However interactivity, animation and event tracking code can and probably
     * should be removed by this method.
     *
     * OK
     */
    void deinitializeImpl() {
        if (isInitialized()) {
            Form f = getComponentForm();
            if (f != null) {
                f.deregisterAnimated(this);
            }
            setInitialized(false);
            setDirtyRegion(null);
            deinitialize();
        }
    }

    /**
     * Invoked to indicate that the component initialization is being reversed
     * since the component was detached from the container hierarchy. This allows
     * the component to deregister animators and cleanup after itself. This
     * method is the opposite of the initComponent() method.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     */
    void deinitialize() {
    }

    /**
     * Allows subclasses to bind functionality that relies on fully initialized and
     * "ready for action" component state
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     */
    void initComponent() {
    }

    /**
     * Indicates if the component is in the initalized state, a component is initialized
     * when its initComponent() method was invoked. The initMethod is invoked before showing the
     * component to the user.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @return true if the component is in the initalized state
     */
    boolean isInitialized() {
        return initialized;
    }

    /**
     * Indicates if the component is in the initalized state, a component is initialized
     * when its initComponent() method was invoked. The initMethod is invoked before showing the
     * component to the user.
     *
     * Nao Presente no JAVADTV 1.1 - Se alguem for estender essa classe pode encontrar problemas de compatibilidade.
     * OK - NAO DA PRA ESTENDER MAIS
     * @param initialized Indicates if the component is in the initalized state
     */
    void setInitialized(boolean initialized) {
        this.initialized = initialized;
    }

    /**
     * @inheritDoc
     *
     * Presente no JAVADTV 1.1
     */
    public void styleChanged(String propertyName, Style source) {
        //changing the Font, Padding, Margin may casue the size of the Component to Change
        //therefore we turn on the shouldCalcPreferredSize flag
        if (!shouldCalcPreferredSize &&
                propertyName.equals(Style.FONT) ||
                propertyName.equals(Style.MARGIN) ||
                propertyName.equals(Style.PADDING)) {
            setShouldCalcPreferredSize(true);
            Container parent = getParent();
            if (parent != null) {
                parent.revalidate();
            }
        }
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the down key
     *
     * Presente no JAVADTV 1.1
     */
    public Component getNextFocusDown() {
        return nextFocusDown;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the down key
     *
     * Presente no JAVADTV 1.1
     */
    public void setNextFocusDown(Component nextFocusDown) {
        this.nextFocusDown = nextFocusDown;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the up key.
     *
     * Presente no JAVADTV 1.1
     */
    public Component getNextFocusUp() {
        return nextFocusUp;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the up key, this method doesn't affect the generl focus beavior.
     *
     * Presente no JAVADTV 1.1
     */
    public void setNextFocusUp(Component nextFocusUp) {
        this.nextFocusUp = nextFocusUp;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the left key.
     *
     * Presente no JAVADTV 1.1
     */
    public Component getNextFocusLeft() {
        return nextFocusLeft;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the left key, this method doesn't affect the generl focus beavior.
     *
     * Presente no JAVADTV 1.1
     */
    public void setNextFocusLeft(Component nextFocusLeft) {
        this.nextFocusLeft = nextFocusLeft;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the right key
     *
     * Presente no JAVADTV 1.1
     */
    public Component getNextFocusRight() {
        return nextFocusRight;
    }

    /**
     * Allows us to determine which component will receive focus next when travering
     * with the right key
     *
     * Presente no JAVADTV 1.1
     */
    public void setNextFocusRight(Component nextFocusRight) {
        this.nextFocusRight = nextFocusRight;
    }

    /**
     * Indicates whether component is enabled or disabled thus allowing us to prevent
     * a component from receiving input events and indicate so visually
     *
     * Presente no JAVADTV 1.1
     */
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * Used to reduce coupling between the TextArea component and display/implementation
     * classes thus reduce the size of the hello world MIDlet
     *
     * Nao Presente no JAVADTV 1.1 - Codigo usado para executar um exemplo, devera ser removido
     * OK - NAO DA PRA ESTENDER MAIS
     * @param text text after editing is completed
     */
    void onEditComplete(String text) {
    }

    /**
     * Indicates whether component is enabled or disabled thus allowing us to prevent
     * a component from receiving input events and indicate so visually
     *
     * Presente no JAVADTV 1.1
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        Form f = getComponentForm();
        if (f != null) {
            f.clearFocusVectors();
            repaint();
        }
    }

    public int getAnimationMode() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public int getDelay() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public int getPosition() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public int getRepetitionMode() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public boolean isRunning() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void jumpTo(int position) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void setAnimationMode(int mode) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void setDelay(int n) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void setRepetitionMode(int n) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void start() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void stop() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    class BGPainter implements Painter {

        private Form parent;
        private Form previousTint;
        private boolean ignorCoordinates;
        private Painter painter;

        public BGPainter() {
        }

        public BGPainter(Form parent, Painter p) {
            this.painter = p;
            this.parent = parent;
        }

        public void setIgnorCoordinates(boolean ignorCoordinates) {
            this.ignorCoordinates = ignorCoordinates;
        }

        public void setPreviousForm(Form previous) {
            previousTint = previous;
        }

        public void paint(Graphics g, Rectangle rect) {
            if (painter != null) {
                if (previousTint != null) {
                    previousTint.paint(g);
                }
                Dimension d = rect.getSize();
                int width = d.getWidth();
                int height = d.getHeight();
                int x = rect.getX();
                int y = rect.getY();

                if (ignorCoordinates) {
                    // this is a special case for dialogs since they are "pushed" to
                    // a position in the screen and can't draw behind their title
                    // we need to still "pretend" that they own the screen...
                    x = 0;
                    y = 0;
                    width = parent.getWidth();
                    height = parent.getHeight();
                    int transY = g.getTranslateY();
                    g.translate(0, -transY);

                    painter.paint(g, new Rectangle(x, y, width, height));
                    g.translate(0, transY);
                } else {
                    painter.paint(g, new Rectangle(x, y, width, height));
                }
            } else {
                Style s = getStyle();
                int x = rect.getX();
                int y = rect.getY();
                int width = rect.getSize().getWidth();
                int height = rect.getSize().getHeight();
                if (width <= 0 || height <= 0) {
                    return;
                }
                Image bgImage = s.getBgImage();
                if (bgImage == null) {
                    if (hasFocus() && isFocusPainted()) {
                        g.setColor(s.getBgSelectionColor());
                        g.fillRect(x, y, width, height, s.getBgTransparency());
                    } else {
                        g.setColor(s.getBgColor());
                        g.fillRect(x, y, width, height, s.getBgTransparency());
                    }
                } else {
                    if (getStyle().isScaleImage()) {
                        if (bgImage.getWidth() != width || bgImage.getHeight() != height) {
                            bgImage = bgImage.scaled(width, height);
                            s.setBgImage(bgImage, true);
                        }
                    } else {
                        int iW = bgImage.getWidth();
                        int iH = bgImage.getHeight();
                        for (int xPos = 0; xPos < width; xPos += iW) {
                            for (int yPos = 0; yPos < height; yPos += iH) {
                                g.drawImage(s.getBgImage(), x + xPos, y + yPos);
                            }
                        }
                        return;
                    }
                    g.drawImage(s.getBgImage(), x, y);
                }
            }
        }
    }
}
